package org.example.dev.service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base32;

//生成随机6位数字验证码
public class VerificationCodeGenerator {

    /*
    private static final String CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final int CODE_LENGTH = 4; // 验证码长度
    public static String generateVerificationCode() {
        StringBuilder codeBuilder = new StringBuilder(CODE_LENGTH);
        Random random = new Random();

        for (int i = 0; i < CODE_LENGTH; i++) {
            // 生成随机索引并从字符集中选择字符
            int index = random.nextInt(CHARACTERS.length());
            codeBuilder.append(CHARACTERS.charAt(index));
        }

        // 返回生成的验证码字符串
        return codeBuilder.toString();
    }

    */

    //HMAC算法名称
    private static final String HMAC_ALGORITHM = "HmacSHA1";
    // 验证码长度为6位
    private static final int OTP_LENGTH = 6;
    // 时间步长为120秒,验证码有效时长为120s
    private static final long TIME_STEP = 120L;
    //Base32编码的密钥，应该是一个有效的Base32字符串
    private static final String SECRET_KEY ="ECVHHBNK6POD3WWYBUVYU57736QVAK74";

    /**
     * 生成基于时间的一次性密码（TOTP）
     *
     * @return 生成的6位数字验证码
     * @throws NoSuchAlgorithmException 如果没有找到指定的加密算法
     * @throws InvalidKeyException      如果密钥无效
     */
    public static String generateTotp()
            throws NoSuchAlgorithmException, InvalidKeyException {

        //将Base32编码的密钥解码为字节数组
        byte[] key = new Base32().decode(SECRET_KEY);

        //获取当前时间戳，并将其转换为以TIME_STEP为单位的时间计数器值
        long currentTimeStep = System.currentTimeMillis() / 1000L / TIME_STEP;

        //创建一个HMAC-SHA1的消息认证码对象
        Mac mac = Mac.getInstance(HMAC_ALGORITHM);
        //使用提供的密钥初始化消息认证码对象
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, HMAC_ALGORITHM);
        mac.init(secretKeySpec);

        //将时间计数器值转换为8字节的字节数
        byte[] data = ByteBuffer.allocate(8).putLong(currentTimeStep).array();
        //计算HMAC哈希值
        byte[] hash = mac.doFinal(data);

        //根据RFC4226定义的方法从哈希值中提取一个整数值
        int offset = hash[hash.length - 1] & 0xf;//取最后一位作为偏移
        int binary = ((hash[offset] & 0x7f) << 24) |// 只取最高位的7个bit
                ((hash[offset + 1] & 0xff) << 16) |
                ((hash[offset + 2] & 0xff) << 8) |
                (hash[offset + 3] & 0xff);
        // 将得到的整数值对10^OTP_LENGTH取模，确保结果是OTP_LENGTH位数
        int otp = binary % 1000000;
        //返回格式化后的6位数验证码，不足的地方用前导零填充
        return String.format("%0" + OTP_LENGTH + "d", otp);
    }
}